#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "MSTK.h"


#ifdef __cplusplus
extern "C" {
#endif

  /* 
     This function send mesh to processor rank in communicator comm
     attr list is sent but no attribute value is sent, call MESH_SendAttr().
  */
int MESH_Vol_SendMesh(Mesh_ptr mesh, int rank, MPI_Comm comm);
int MESH_Surf_SendMesh(Mesh_ptr mesh, int rank, MPI_Comm comm);

int MESH_SendMesh(Mesh_ptr mesh, int rank, MPI_Comm comm) {
  int nf, nr;
  /* basic mesh information */
  nf = MESH_Num_Faces(mesh);
  nr = MESH_Num_Regions(mesh);
  if (nr)
    MESH_Vol_SendMesh(mesh,rank,comm);
  else if(nf) 
    MESH_Surf_SendMesh(mesh,rank,comm);
  else {
    MSTK_Report("MESH_SendMesh()","only send volume or surface mesh",ERROR);
    exit(-1);
  }
  return 1;
}

int MESH_Surf_SendMesh(Mesh_ptr mesh, int rank, MPI_Comm comm) {
  int i, j, nv, nf, mesh_info[5], nfvs, nfv, natt, ncomp;
  MVertex_ptr mv;
  MFace_ptr mf;
  List_ptr mfverts;
  RepType rtype;
  char attname[256];
  MType mtype;
  MAttType att_type;
  MAttrib_ptr attrib;
  int* list_attr;
  char* list_attr_names;
  double coor[3];
  /* mesh_info store the mesh reptype, nv, nf, nfvs and natt */
  rtype = MESH_RepType(mesh);
  nv = MESH_Num_Vertices(mesh);
  nf = MESH_Num_Faces(mesh);

  mesh_info[0] = rtype;
  mesh_info[1] = nv;
  mesh_info[2] = nf;

  /* collect data */
  int *list_vertex = (int *)MSTK_malloc(3*nv*sizeof(int));
  double *list_coor = (double *)MSTK_malloc(3*nv*sizeof(double));
  for(i = 0; i < nv; i++) {
    mv = MESH_Vertex(mesh,i);
    list_vertex[3*i] = (MV_GEntID(mv)<<3) | (MV_GEntDim(mv));
    list_vertex[3*i+1] = (MV_MasterParID(mv) <<2) | (MV_PType(mv));
    list_vertex[3*i+2] = MV_GlobalID(mv);
    MV_Coords(mv,coor);
    list_coor[i*3] = coor[0];
    list_coor[i*3+1] = coor[1];
    list_coor[i*3+2] = coor[2];
  }


  int *list_face = (int *)MSTK_malloc(MAXPV2*nf*sizeof(int));

  nfvs = 0;
  /* first int store nfv, then their ids, then the 3 field */
  for(i = 0; i < nf; i++) {
    mf = MESH_Face(mesh,i);
    mfverts = MF_Vertices(mf,1,0);
    nfv = List_Num_Entries(mfverts);
    list_face[nfvs] = nfv;
    for(j = 0; j < nfv; j++) 
      list_face[nfvs+j+1] = MV_ID(List_Entry(mfverts,j));
    list_face[nfvs+nfv+1] = (MF_GEntID(mf)<<3) | (MF_GEntDim(mf));
    list_face[nfvs+nfv+2] = (MF_MasterParID(mf) <<2) | (MF_PType(mf));
    list_face[nfvs+nfv+3] = MF_GlobalID(mf);
    nfvs += (nfv + 4);
    List_Delete(mfverts);
  }
  mesh_info[3] = nfvs;

  /* number of attr */
  natt = MESH_Num_Attribs(mesh);
  mesh_info[4] = natt;

  /* collect attrs */
  if(natt) {
    list_attr = (int *)MSTK_malloc((natt)*sizeof(int));
    list_attr_names = (char *)MSTK_malloc((natt)*256);
    
    for(i = 0; i < natt; i++) {
      attrib = MESH_Attrib(mesh,i);
      MAttrib_Get_Name(attrib,attname);
      att_type = MAttrib_Get_Type(attrib);
      ncomp = MAttrib_Get_NumComps(attrib);
      mtype = MAttrib_Get_EntDim(attrib);
      list_attr[i] = (ncomp << 6) | (mtype << 3) | (att_type);
      strcpy(&list_attr_names[i*256],attname);
    }
  }

  /* send mesh_info */
  MPI_Send(mesh_info,5,MPI_INT,rank,rank,comm);

  /* send vertices */
  /* printf("%d vertices sent to rank %d\n",nv,rank); */
  MPI_Send(list_vertex,3*nv,MPI_INT,rank,rank,comm);
  MPI_Send(list_coor,3*nv,MPI_DOUBLE,rank,rank,comm);

  /* send faces */
  /* printf("%d faces sent to rank %d\n",nf,rank); */
  MPI_Send(list_face,nfvs,MPI_INT,rank,rank,comm);
  
  /* send attr */
  /* printf("%d attrs sent to rank %d\n",natt,rank); */
  if(natt) {
    MPI_Send(list_attr,natt,MPI_INT,rank,rank,comm);
    MPI_Send(list_attr_names,natt*256,MPI_CHAR,rank,rank,comm);
    MSTK_free(list_attr);
    MSTK_free(list_attr_names);
  }


  MSTK_free(list_vertex);
  MSTK_free(list_face);
  MSTK_free(list_coor);
  return 1;
}


int MESH_Vol_SendMesh(Mesh_ptr mesh, int rank, MPI_Comm comm) {
  int i, j, nv, nr, mesh_info[5], nrvs, nrv, natt, ncomp;
  MVertex_ptr mv;
  MRegion_ptr mr;
  List_ptr mrverts;
  RepType rtype;
  char attname[256];
  MType mtype;
  MAttType att_type;
  MAttrib_ptr attrib;
  int *list_attr;
  char *list_attr_names;
  double coor[3];

  /* mesh_info store the mesh reptype, nv, nf, nfvs and natt */
  rtype = MESH_RepType(mesh);
  nv = MESH_Num_Vertices(mesh);
  nr = MESH_Num_Regions(mesh);
  mesh_info[0] = rtype;
  mesh_info[1] = nv;
  mesh_info[2] = nr;
  /* collect data */
  int *list_vertex = (int *)MSTK_malloc(3*nv*sizeof(int));
  double *list_coor = (double *)MSTK_malloc(3*nv*sizeof(double));
  for(i = 0; i < nv; i++) {
    mv = MESH_Vertex(mesh,i);
    list_vertex[3*i] = (MV_GEntID(mv)<<3) | (MV_GEntDim(mv));
    list_vertex[3*i+1] = (MV_MasterParID(mv) <<2) | (MV_PType(mv));
    list_vertex[3*i+2] = MV_GlobalID(mv);
    MV_Coords(mv,coor);
    list_coor[i*3] = coor[0];
    list_coor[i*3+1] = coor[1];
    list_coor[i*3+2] = coor[2];

  }


  int *list_region = (int *)MSTK_malloc(MAXPV3*nr*sizeof(int));

  nrvs = 0;
  /* first int store nrv, then their ids, then the 3 field */
  for(i = 0; i < nr; i++) {
    mr = MESH_Region(mesh,i);
    mrverts = MR_Vertices(mr);
    nrv = List_Num_Entries(mrverts);
    list_region[nrvs] = nrv;
    for(j = 0; j < nrv; j++) 
      list_region[nrvs+j+1] = MV_ID(List_Entry(mrverts,j));
    list_region[nrvs+nrv+1] = (MR_GEntID(mr)<<3) | (MR_GEntDim(mr));
    list_region[nrvs+nrv+2] = (MR_MasterParID(mr) <<2) | (MR_PType(mr));
    list_region[nrvs+nrv+3] = MR_GlobalID(mr);
    nrvs += (nrv + 4);
    List_Delete(mrverts);
  }
  mesh_info[3] = nrvs;

  /* number of attr */
  natt = MESH_Num_Attribs(mesh);
  mesh_info[4] = natt;

  /* collect attrs */
  if(natt) {
    list_attr = (int *)MSTK_malloc((natt+1)*sizeof(int));
    list_attr_names = (char *)MSTK_malloc((natt+1)*256);
    for(i = 0; i < natt; i++) {
      attrib = MESH_Attrib(mesh,i);
      MAttrib_Get_Name(attrib,attname);
      att_type = MAttrib_Get_Type(attrib);
      ncomp = MAttrib_Get_NumComps(attrib);
      mtype = MAttrib_Get_EntDim(attrib);
      list_attr[i] = (ncomp << 6) | (mtype << 3) | (att_type);
      strcpy(&list_attr_names[i*256],attname);
    }
  }
  /* send mesh_info */
  MPI_Send(mesh_info,5,MPI_INT,rank,rank,comm);

  /* send vertices */
  /* printf("%d vertices sent to rank %d\n",nv,rank); */
  MPI_Send(list_vertex,3*nv,MPI_INT,rank,rank,comm);
  MPI_Send(list_coor,3*nv,MPI_DOUBLE,rank,rank,comm);

  /* send regions */
  /* printf("%d regions sent to rank %d\n",nr,rank); */
  MPI_Send(list_region,nrvs,MPI_INT,rank,rank,comm);

  /* send attr */
  /* printf("%d attr sent to rank %d\n",natt,rank); */
  if(natt) {
    MPI_Send(list_attr,natt,MPI_INT,rank,rank,comm);
    MPI_Send(list_attr_names,natt*256,MPI_CHAR,rank,rank,comm);
    MSTK_free(list_attr);
    MSTK_free(list_attr_names);
  }
  MSTK_free(list_vertex);
  MSTK_free(list_region);
  MSTK_free(list_coor);
  return 1;
}
  
#ifdef __cplusplus
}
#endif

